/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.netbeans.editor.ext; import java.lang.reflect.Modifier; import java.util.Map; import java.util.List; import java.util.Comparator; import java.util.Iterator; import java.util.HashMap; import java.util.ArrayList; import org.netbeans.editor.BaseDocument; /** * Java completion query specifications * * @author Miloslav Metelka * @version 1.00 */ public class JCompletion { public static final int PUBLIC_LEVEL = 3; public static final int PROTECTED_LEVEL = 2; public static final int PACKAGE_LEVEL = 1; public static final int PRIVATE_LEVEL = 0; public static final SimpleClass BOOLEAN_CLASS = new SimpleClass("boolean", ""); // NOI18N public static final SimpleClass BYTE_CLASS = new SimpleClass("byte", ""); // NOI18N public static final SimpleClass CHAR_CLASS = new SimpleClass("char", ""); // NOI18N public static final SimpleClass DOUBLE_CLASS = new SimpleClass("double", ""); // NOI18N public static final SimpleClass FLOAT_CLASS = new SimpleClass("float", ""); // NOI18N public static final SimpleClass INT_CLASS = new SimpleClass("int", ""); // NOI18N public static final SimpleClass LONG_CLASS = new SimpleClass("long", ""); // NOI18N public static final SimpleClass SHORT_CLASS = new SimpleClass("short", ""); // NOI18N public static final SimpleClass VOID_CLASS = new SimpleClass("void", ""); // NOI18N public static final BaseType BOOLEAN_TYPE = new BaseType(BOOLEAN_CLASS, 0); public static final BaseType BYTE_TYPE = new BaseType(BYTE_CLASS, 0); public static final BaseType CHAR_TYPE = new BaseType(CHAR_CLASS, 0); public static final BaseType DOUBLE_TYPE = new BaseType(DOUBLE_CLASS, 0); public static final BaseType FLOAT_TYPE = new BaseType(FLOAT_CLASS, 0); public static final BaseType INT_TYPE = new BaseType(INT_CLASS, 0); public static final BaseType LONG_TYPE = new BaseType(LONG_CLASS, 0); public static final BaseType SHORT_TYPE = new BaseType(SHORT_CLASS, 0); public static final BaseType VOID_TYPE = new BaseType(VOID_CLASS, 0); public static final SimpleClass INVALID_CLASS = new SimpleClass("", ""); // NOI18N public static final BaseType INVALID_TYPE = new BaseType(INVALID_CLASS, 0); public static final SimpleClass NULL_CLASS = new SimpleClass("null", ""); // NOI18N public static final BaseType NULL_TYPE = new BaseType(NULL_CLASS, 0); public static final SimpleClass OBJECT_CLASS = new SimpleClass("java.lang.Object", "java.lang".length(), true); // NOI18N public static final BaseType OBJECT_TYPE = new BaseType(OBJECT_CLASS, 0); public static final SimpleClass CLASS_CLASS = new SimpleClass("java.lang.Class", "java.lang".length(), true); // NOI18N public static final BaseType CLASS_TYPE = new BaseType(CLASS_CLASS, 0); public static final SimpleClass STRING_CLASS = new SimpleClass("java.lang.String", "java.lang".length(), true); // NOI18N public static final BaseType STRING_TYPE = new BaseType(STRING_CLASS, 0); static final int INTERFACE_BIT = (1 << 30); // no neg nums in modifiers static final int INTERFACE_BIT_FILTER = (~INTERFACE_BIT); private static final SimpleClass[] primitiveClasses = new SimpleClass[] { BOOLEAN_CLASS, BYTE_CLASS, CHAR_CLASS, DOUBLE_CLASS, FLOAT_CLASS, INT_CLASS, LONG_CLASS, SHORT_CLASS, VOID_CLASS }; private static final BaseType[] primitiveTypes = new BaseType[] { BOOLEAN_TYPE, BYTE_TYPE, CHAR_TYPE, DOUBLE_TYPE, FLOAT_TYPE, INT_TYPE, LONG_TYPE, SHORT_TYPE, VOID_TYPE }; public static final JCParameter[] EMPTY_PARAMETERS = new JCParameter[0]; public static final JCClass[] EMPTY_CLASSES = new JCClass[0]; public static final JCPackage[] EMPTY_PACKAGES = new JCPackage[0]; public static final JCField[] EMPTY_FIELDS = new JCField[0]; public static final JCConstructor[] EMPTY_CONSTRUCTORS = new JCConstructor[0]; public static final JCMethod[] EMPTY_METHODS = new JCMethod[0]; private static JCFinder finder; private static int debugMode; /** Map holding the simple class instances * @associates JCClass*/ private static HashMap classCache = new HashMap(5003); /** Map holding the cached types * @associates JCType*/ private static HashMap typeCache = new HashMap(5003); /** Debug expression creation */ public static final int DEBUG_EXP = 1; /** Debug finding packages/classes/fields/methods */ public static final int DEBUG_FIND = 2; /** Get the current default finder */ public static JCFinder getFinder() { return finder; } /** Set the current default finder */ public static void setFinder(JCFinder f) { finder = f; } /** Get level from modifiers. */ public static int getLevel(int modifiers) { if ((modifiers & Modifier.PUBLIC) != 0) { return PUBLIC_LEVEL; } else if ((modifiers & Modifier.PROTECTED) != 0) { return PROTECTED_LEVEL; } else if ((modifiers & Modifier.PRIVATE) == 0) { return PACKAGE_LEVEL; } else { return PRIVATE_LEVEL; } } public static boolean isPrimitiveClassName(String s) { return JavaKeywords.isTypeOrVoid(JavaKeywords.getKeyword(s)); } public static boolean isPrimitiveClass(JCClass c) { return (c.getPackageName().length() == 0) && isPrimitiveClassName(c.getName()); } public static JCClass getPrimitiveClass(String s) { int kwd = JavaKeywords.getKeyword(s); return (JavaKeywords.isTypeOrVoid(kwd)) ? primitiveClasses[kwd] : null; } public static JCType getPrimitiveType(String s) { int kwd = JavaKeywords.getKeyword(s); return (JavaKeywords.isTypeOrVoid(kwd)) ? primitiveTypes[kwd] : null; } public static Iterator getPrimitiveClassIterator() { return new Iterator() { int ind; public boolean hasNext() { return (ind < primitiveClasses.length - 1); } public Object next() { return primitiveClasses[ind++]; } public void remove() { throw new UnsupportedOperationException(); } }; } public static JCClass getSimpleClass(String fullClassName, int packageNameLen) { JCClass cls = (JCClass)classCache.get(fullClassName); if (cls == null // not in cache yet || packageNameLen != cls.getPackageName().length() // different class ) { cls = new SimpleClass(fullClassName, packageNameLen, true); classCache.put(fullClassName, cls); } return cls; } public static JCClass getSimpleClass(JCClass cls) { return getSimpleClass(cls.getFullName(), cls.getPackageName().length()); } public static JCClass createSimpleClass(String fullClassName) { int nameInd = fullClassName.lastIndexOf('.') + 1; return createSimpleClass(fullClassName.substring(nameInd), (nameInd > 0) ? fullClassName.substring(0, nameInd - 1) : ""); // NOI18N } public static JCClass createSimpleClass(String name, String packageName) { return new SimpleClass(name, packageName); } public static JCType createType(JCClass cls, int arrayDepth) { return new BaseType(cls, arrayDepth); } /** Create new type or get the existing one from the cache. The cache holds * the arrays with the increasing array depth for the particular class * as the members. Simple class is used for the caching to make it independent * on the real completion classes that can become obsolete and thus should * be garbage collected. */ public static JCType getType(JCClass cls, int arrayDepth) { if (cls == null) { return null; } JCType[] types = (JCType[])typeCache.get(cls); if (types != null) { if (arrayDepth < types.length) { if (types[arrayDepth] == null) { types[arrayDepth] = new BaseType(types[0].getClazz(), arrayDepth); } } else { // array length depth too small for given array depth cls = types[0].getClazz(); JCType[] tmp = new JCType[arrayDepth + 1]; System.arraycopy(types, 0, tmp, 0, types.length); types = tmp; types[arrayDepth] = new BaseType(cls, arrayDepth); typeCache.put(cls, types); } } else { // types array not yet created cls = getSimpleClass(cls.getFullName(), cls.getPackageName().length()); if (arrayDepth > 0) { types = new JCType[arrayDepth + 1]; types[arrayDepth] = new BaseType(cls, arrayDepth); } else { types = new JCType[2]; } types[0] = new BaseType(cls, 0); typeCache.put(cls, types); } return types[arrayDepth]; } public static class BasePackage implements JCPackage { private String name; private JCClass[] classes; private int dotCnt = -1; private String lastName; public BasePackage(String name) { this(name, EMPTY_CLASSES); } public BasePackage(String name, JCClass[] classes) { this.name = name; this.classes = classes; } /** Get full name of this package */ public final String getName() { return name; } public String getLastName() { if (lastName == null) { lastName = name.substring(name.lastIndexOf('.') + 1); } return lastName; } /** Get classes contained in this package */ public JCClass[] getClasses() { return classes; } public void setClasses(JCClass[] classes) { this.classes = classes; } public int getDotCount() { if (dotCnt < 0) { int i = 0; do { dotCnt++; i = name.indexOf('.', i) + 1; } while (i > 0); } return dotCnt; } public int compareTo(Object o) { if (this == o) { return 0; } JCPackage p = (JCPackage)o; return name.compareTo(p.getName()); } public int hashCode() { return name.hashCode(); } public boolean equals(Object o) { if (this == o) { return true; } if (o instanceof JCPackage) { return name.equals(((JCPackage)o).getName()); } if (o instanceof String) { return name.equals((String)o); } return false; } public String toString() { return name; } } public static class SimpleClass implements JCClass { protected String name; protected String packageName; protected String fullName; public SimpleClass(String name, String packageName) { this.name = name; this.packageName = packageName; if (name == null || packageName == null) { throw new NullPointerException(this.toString()); } } public SimpleClass(String fullName, int packageNameLen, boolean intern) { this.fullName = fullName; if (packageNameLen <= 0) { name = fullName; packageName = ""; // NOI18N } else { // use interned strings here name = fullName.substring(packageNameLen + 1); packageName = fullName.substring(0, packageNameLen); if (intern) { name = name.intern(); packageName = packageName.intern(); } } } SimpleClass() { } public final String getName() { return name; } public final String getPackageName() { return packageName; } public String getFullName() { if (fullName == null) { fullName = (packageName.length() > 0) ? (packageName + "." + name) : name; // NOI18N } return fullName; } public int getTagOffset() { return -1; } public boolean isInterface() { return false; } public int getModifiers() { return 0; } public JCClass getSuperclass() { return null; } public JCClass[] getInterfaces() { return EMPTY_CLASSES; } public JCField[] getFields() { return EMPTY_FIELDS; } public JCConstructor[] getConstructors() { return EMPTY_CONSTRUCTORS; } public JCMethod[] getMethods() { return EMPTY_METHODS; } public int compareTo(Object o) { if (this == o) { return 0; } JCClass c = (JCClass)o; int order = packageName.compareTo(c.getPackageName()); if (order == 0) { order = name.compareTo(c.getName()); } return order; } public int hashCode() { return name.hashCode() ^ packageName.hashCode(); } public boolean equals(Object o) { if (this == o) { return true; } if (o instanceof JCClass) { JCClass c = (JCClass)o; return name.equals(c.getName()) && packageName.equals(c.getPackageName()); } return false; } public String toString() { return (getPackageName().length() > 0) ? getPackageName() + '.' + getName().replace('.', '$') : getName().replace('.', '$'); } } /** Abstract class that assumes lazy initialization */ public static abstract class AbstractClass extends SimpleClass { protected int modifiers; protected Body body; public AbstractClass(String name, String packageName, boolean iface, int modifiers) { super(name, packageName); this.modifiers = modifiers; if (iface) { this.modifiers |= INTERFACE_BIT; } } AbstractClass() { super(); } /** Init internal representation */ protected abstract void init(); /** Is this class an interface? */ public boolean isInterface() { return ((modifiers & INTERFACE_BIT) != 0); } /** Get modifiers for this class */ public int getModifiers() { return modifiers & INTERFACE_BIT_FILTER; } public int getTagOffset() { if (body == null) { init(); } return body.tagOffset; } /** Get superclass of this class */ public JCClass getSuperclass() { if (body == null) { init(); } return body.superClass; } /** Get interfaces this class implements */ public JCClass[] getInterfaces() { if (body == null) { init(); } return body.interfaces; } /** Get fields that this class contains */ public JCField[] getFields() { if (body == null) { init(); } return body.fields; } /** Get constructors that this class contains */ public JCConstructor[] getConstructors() { if (body == null) { init(); } return body.constructors; } /** Get methods that this class contains */ public JCMethod[] getMethods() { if (body == null) { init(); } return body.methods; } public static class Body { public int tagOffset; public JCClass superClass; public JCClass[] interfaces; public JCField[] fields; public JCConstructor[] constructors; public JCMethod[] methods; } } /** Description of the type */ public static class BaseType implements JCType { protected JCClass clazz; protected int arrayDepth; public BaseType(JCClass clazz, int arrayDepth) { this.clazz = clazz; this.arrayDepth = arrayDepth; if (arrayDepth < 0) { throw new IllegalArgumentException("Array depth " + arrayDepth + " < 0."); // NOI18N } } BaseType() { } public JCClass getClazz() { return clazz; } public int getArrayDepth() { return arrayDepth; } public String format(boolean useFullName) { StringBuffer sb = new StringBuffer(useFullName ? getClazz().getFullName() : getClazz().getName()); int ad = arrayDepth; while (ad > 0) { sb.append("[]"); // NOI18N ad--; } return sb.toString(); } public int compareTo(Object o) { if (this == o) { return 0; } JCType t = (JCType)o; int order = clazz.compareTo(t.getClazz()); if (order == 0) { order = arrayDepth - t.getArrayDepth(); } return order; } public int hashCode() { return clazz.hashCode() + arrayDepth; } public boolean equals(Object o) { if (this == o) { return true; } if (o instanceof JCType) { JCType t = (JCType)o; return clazz.equals(t.getClazz()) && arrayDepth == t.getArrayDepth(); } return false; } public String toString() { StringBuffer sb = new StringBuffer(clazz.toString()); int ad = arrayDepth; while (ad > 0) { sb.append("[]"); // NOI18N ad--; } return sb.toString(); } } /** Description of the method parameter */ public static class BaseParameter implements JCParameter { protected String name; protected JCType type; public BaseParameter(String name, JCType type) { this.name = name; this.type = type; } BaseParameter() { } /** Name of the parameter */ public String getName() { return name; } /** Type of the parameter */ public JCType getType() { return type; } public int compareTo(Object o) { if (this == o) { return 0; } JCParameter p = (JCParameter)o; return type.compareTo(p.getType()); // only by type } public int hashCode() { return type.hashCode() ^ name.hashCode(); } public boolean equals(Object o) { if (this == o) { return true; } if (o instanceof JCParameter) { JCParameter p = (JCParameter)o; return type.equals(p.getType()); // only by type } return false; } public String toString() { return type.toString() + ' ' + name; } } public static class BaseField extends BaseParameter implements JCField { protected JCClass clazz; protected int modifiers; protected int tagOffset; public BaseField(JCClass clazz, String name, JCType type, int modifiers) { super(name, type); this.clazz = clazz; this.modifiers = modifiers; } BaseField() { } public int getModifiers() { return modifiers; } public JCClass getClazz() { return clazz; } public int getTagOffset() { return tagOffset; } public int compareTo(Object o) { if (this == o) { return 0; } JCField f = (JCField)o; int order = super.compareTo(o); if (order == 0) { order = name.compareTo(f.getName()); } return order; } public int hashCode() { return type.hashCode() ^ name.hashCode() ^ modifiers; } public boolean equals(Object o) { if (this == o) { return true; } if (o instanceof JCField) { JCField p = (JCField)o; return name.equals(p.getName()) && type.equals(p.getType()); } return false; } public String toString() { return Modifier.toString(modifiers) + ' ' + super.toString(); } } public static class BaseConstructor implements JCConstructor { protected JCClass clazz; protected int tagOffset; protected int modifiers; protected JCParameter[] parameters; protected JCClass[] exceptions; public BaseConstructor(JCClass clazz, int modifiers, JCParameter[] parameters, JCClass[] exceptions) { this.clazz = clazz; this.modifiers = modifiers; this.parameters = parameters; this.exceptions = exceptions; } BaseConstructor() { } public JCClass getClazz() { return clazz; } public int getTagOffset() { return tagOffset; } public int getModifiers() { return modifiers; } public JCParameter[] getParameters() { return parameters; } public JCClass[] getExceptions() { return exceptions; } /** This implementation expects * that only the constructors inside one class will * be compared. */ public int compareTo(Object o) { if (this == o) { return 0; } JCConstructor c = (JCConstructor)o; int order = 0; JCParameter[] mp = c.getParameters(); int commonCnt = Math.min(parameters.length, mp.length); for (int i = 0; i < commonCnt; i++) { order = parameters[i].compareTo(mp[i]); if (order != 0) { return order; } } order = parameters.length - mp.length; return order; } public boolean equals(Object o) { if (this == o) { return true; } if (o instanceof JCConstructor) { return (compareTo(o) == 0); } return false; } public int hashCode() { int h = 0; for (int i = 0; i < parameters.length; i++) { h ^= parameters[i].hashCode(); } return h; } String toString(String returnTypeName, String methodName) { StringBuffer sb = new StringBuffer(Modifier.toString(modifiers)); sb.append(' '); sb.append(returnTypeName); sb.append(methodName); // Add parameters sb.append('('); int cntM1 = parameters.length - 1; for (int i = 0; i <= cntM1; i++) { sb.append(parameters[i].toString()); if (i < cntM1) { sb.append(", "); // NOI18N } } sb.append(')'); // Add exceptions cntM1 = exceptions.length - 1; if (cntM1 >= 0) { sb.append(" throws "); // NOI18N for (int i = 0; i <= cntM1; i++) { sb.append(exceptions[i].toString()); if (i < cntM1) { sb.append(", "); // NOI18N } } } return sb.toString(); } public String toString() { return toString("", getClazz().getName()); // NOI18N } } public static class BaseMethod extends BaseConstructor implements JCMethod { protected String name; protected JCType returnType; public BaseMethod(JCClass clazz, String name, int modifiers, JCType returnType, JCParameter[] parameters, JCClass[] exceptions) { super(clazz, modifiers, parameters, exceptions); this.name = name; this.returnType = returnType; } BaseMethod() { } public String getName() { return name; } public JCType getReturnType() { return returnType; } public int compareTo(Object o) { if (this == o) { return 0; } JCMethod m = (JCMethod)o; int order = name.compareTo(m.getName()); if (order == 0) { order = super.compareTo(o); } return order; } public int hashCode() { return name.hashCode() ^ super.hashCode(); } public boolean equals(Object o) { if (this == o) { return true; } if (o instanceof JCMethod) { return (compareTo(o) == 0); } return false; } public String toString() { String rtn = getReturnType().toString(); return toString((rtn.length() > 0) ? rtn + ' ' : "", name); // NOI18N } } public abstract static class AbstractProvider implements JCClassProvider { public abstract Iterator getClasses(); public boolean append(JCClassProvider cp) { Iterator i = cp.getClasses(); while (i.hasNext()) { JCClass c = (JCClass)i.next(); if (!cp.notifyAppend(c, false)) { return false; } if (!appendClass(c)) { return false; } if (!cp.notifyAppend(c, true)) { return false; } } return true; } protected boolean appendClass(JCClass c) { return true; } public void reset() { } /** This method is executed by the target Class Provider * to notify this provider about the class appending. * @param c JC class that was appended * @return true to continue building, false to stop build */ public boolean notifyAppend(JCClass c, boolean appendFinished) { return true; } } public static class ListProvider extends AbstractProvider { /** * @associates JCClass */ private List classList; public ListProvider() { classList = new ArrayList(); } public ListProvider(List classList) { this.classList = classList; } protected boolean appendClass(JCClass c) { classList.add(c); return true; } public Iterator getClasses() { return classList.iterator(); } public int getClassCount() { return classList.size(); } } public static class SingleProvider extends AbstractProvider implements Iterator { JCClass c; boolean next = true; public SingleProvider(JCClass c) { this.c = c; } public Iterator getClasses() { if (next) { return this; } else { throw new IllegalStateException(); } } public boolean hasNext() { return next; } public Object next() { next = false; return c; } public void remove() { throw new UnsupportedOperationException(); } } public static int getDebugMode() { return debugMode; } public static void setDebugMode(int newDebugMode) { debugMode = newDebugMode; } } /* * Log * 15 Gandalf 1.14 1/13/00 Miloslav Metelka Localization * 14 Gandalf 1.13 12/28/99 Miloslav Metelka * 13 Gandalf 1.12 11/24/99 Miloslav Metelka * 12 Gandalf 1.11 11/14/99 Miloslav Metelka * 11 Gandalf 1.10 11/8/99 Miloslav Metelka * 10 Gandalf 1.9 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 9 Gandalf 1.8 10/10/99 Miloslav Metelka * 8 Gandalf 1.7 9/30/99 Miloslav Metelka * 7 Gandalf 1.6 9/15/99 Miloslav Metelka * 6 Gandalf 1.5 8/18/99 Miloslav Metelka * 5 Gandalf 1.4 7/30/99 Miloslav Metelka * 4 Gandalf 1.3 7/22/99 Miloslav Metelka * 3 Gandalf 1.2 7/20/99 Miloslav Metelka * 2 Gandalf 1.1 6/10/99 Miloslav Metelka * 1 Gandalf 1.0 6/8/99 Miloslav Metelka * $ */